package org.budo.support.javax.sql.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author lmw
 */
public class JdbcUtil {
    private static final Logger log = LoggerFactory.getLogger(JdbcUtil.class);

    public static Integer executeUpdate(DataSource dataSource, String sql, Object[] parameters) {
        Connection connection = getConnection(dataSource);

        PreparedStatement preparedStatement = prepareStatement(connection, sql);
        setParameters(preparedStatement, parameters);

        try {
            return preparedStatement.executeUpdate();
        } catch (Throwable e) {
            throw new RuntimeException("#30 sql=" + sql + ", parameters=" + Arrays.toString(parameters) + ", e=" + e, e);
        } finally {
            close(preparedStatement);
            close(connection);
        }
    }

    /**
     * 连接数据库并执行 SELECT 1, 失败时报错
     */
    public static void checkConnectMysql(String host, Integer port, String username, String password) {
        String url = "jdbc:mysql://" + host + ":" + port;

        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            connection = DriverManager.getConnection(url, username, password);
            statement = connection.createStatement();
            resultSet = statement.executeQuery("SELECT 1");
        } catch (Throwable e) {
            throw new RuntimeException("#36 checkConnectMysql error, host=" + host + ", port=" + port + ", username=" + username + ", e=" + e, e);
        } finally {
            close(resultSet);
            close(statement);
            close(connection);
        }
    }

    public static Connection getConnection(DataSource dataSource) {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static PreparedStatement prepareStatement(Connection connection, String sql) {
        try {
            return connection.prepareStatement(sql);
        } catch (SQLException e) {
            throw new RuntimeException("#73 sql=" + sql + ", e=" + e, e);
        }
    }

    public static ResultSet executeQuery(PreparedStatement preparedStatement) {
        try {
            return preparedStatement.executeQuery();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static Integer executeUpdate(PreparedStatement preparedStatement) {
        try {
            return preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static PreparedStatement setParameters(PreparedStatement preparedStatement, Object[] parameters) {
        if (null == preparedStatement) {
            log.error("#53 preparedStatement=" + preparedStatement + ", parameters=" + parameters);
            return preparedStatement;
        }

        if (null == parameters || parameters.length < 1) {
            return preparedStatement;
        }

        for (int i = 0; i < parameters.length; i++) {
            int parameterIndex = i + 1;
            Object parameterValue = parameters[i];
            setParameter(preparedStatement, parameterIndex, parameterValue);
        }

        return preparedStatement;
    }

    private static void setParameter(PreparedStatement preparedStatement, Integer parameterIndex, Object parameterValue) {
        try {
            preparedStatement.setObject(parameterIndex, parameterValue);
        } catch (SQLException e) {
            throw new RuntimeException("#57 preparedStatement=" + preparedStatement + ", parameterIndex=" + parameterIndex + ", parameterValue=" + parameterValue + ", e=" + e, e);
        }
    }

    public static void close(Statement statement) {
        if (null == statement) {
            log.error("#94 close, statement=" + statement);
            return;
        }

        try {
            statement.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static void closeQuietly(Statement statement) {
        if (null == statement) {
            log.error("#94 close, statement=" + statement);
            return;
        }

        try {
            statement.close();
        } catch (SQLException e) {
            log.error("#118 closeQuietly error, e=" + e, e);
        }
    }

    public static void close(Connection connection) {
        if (null == connection) {
            log.error("#107 close, connection=" + connection);
            return;
        }

        try {
            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static void close(ResultSet resultSet) {
        if (null == resultSet) {
            log.error("#120 close, resultSet=" + resultSet);
            return;
        }

        try {
            resultSet.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getHost(String url) {
        int a = url.indexOf("://");
        if (a <= 0) {
            throw new IllegalArgumentException("url=" + url);
        }

        int b = url.indexOf(':', a + 3);
        if (b <= 0) {
            b = url.indexOf('/', a + 3);
        }

        if (b <= 0) {
            b = url.length();
        }

        return url.substring(a + 3, b);
    }

    public static Integer getPort(String url) {
        String host = getHost(url);

        int a = url.indexOf("://" + host + ":");
        if (a <= 0) {
            return null;
        }

        int b = url.indexOf('/', a + 4 + host.length());
        if (b <= 0) {
            b = url.length();
        }

        String port = url.substring(a + 4 + host.length(), b);
        return Integer.valueOf(port);
    }
}